package database

import (
	"fmt"
	"log"

	"iot-cloud-platform/internal/config"
	"iot-cloud-platform/internal/models"

	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// InitPostgres initializes PostgreSQL connection with GORM
func InitPostgres(cfg config.DatabaseConfig) (*gorm.DB, error) {
	dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=%s TimeZone=UTC",
		cfg.Host, cfg.User, cfg.Password, cfg.DBName, cfg.Port, cfg.SSLMode)

	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info),
	})
	if err != nil {
		return nil, fmt.Errorf("failed to connect to PostgreSQL: %w", err)
	}

	// Configure connection pool
	sqlDB, err := db.DB()
	if err != nil {
		return nil, fmt.Errorf("failed to get underlying sql.DB: %w", err)
	}

	sqlDB.SetMaxIdleConns(10)
	sqlDB.SetMaxOpenConns(100)

	// Test connection
	if err := sqlDB.Ping(); err != nil {
		return nil, fmt.Errorf("failed to ping PostgreSQL: %w", err)
	}

	// Auto-migrate all models
	if err := autoMigrate(db); err != nil {
		return nil, fmt.Errorf("failed to auto-migrate: %w", err)
	}

	log.Println("PostgreSQL connected and migrations completed successfully")
	return db, nil
}

// autoMigrate runs database migrations for all models
func autoMigrate(db *gorm.DB) error {
	// Enable UUID extension for PostgreSQL
	if err := db.Exec("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"").Error; err != nil {
		return fmt.Errorf("failed to create uuid extension: %w", err)
	}

	// Auto-migrate all models
	err := db.AutoMigrate(
		&models.User{},
		&models.Role{},
		&models.Permission{},
		&models.UserSession{},
		&models.Organization{},
		&models.UserOrganization{},
		&models.Device{},
		&models.DeviceShadow{},
		&models.DeviceCredentials{},
		&models.Command{},
		&models.Rule{},
		&models.Webhook{},
		&models.WebhookLog{},
		&models.Alert{},
		&models.EventLog{},
		&models.DataRetentionPolicy{},
		&models.APIKey{},
		&models.Notification{},
		&models.AuditLog{},
	)

	if err != nil {
		return fmt.Errorf("auto-migration failed: %w", err)
	}

	// Create default data
	if err := createDefaultData(db); err != nil {
		return fmt.Errorf("failed to create default data: %w", err)
	}

	return nil
}

// createDefaultData creates default roles, permissions, and admin user
func createDefaultData(db *gorm.DB) error {
	// Create default permissions
	permissions := []models.Permission{
		{Name: "devices:read", Resource: "devices", Action: "read", Description: "Read device information"},
		{Name: "devices:write", Resource: "devices", Action: "write", Description: "Create and update devices"},
		{Name: "devices:delete", Resource: "devices", Action: "delete", Description: "Delete devices"},
		{Name: "telemetry:read", Resource: "telemetry", Action: "read", Description: "Read telemetry data"},
		{Name: "commands:send", Resource: "commands", Action: "write", Description: "Send commands to devices"},
		{Name: "users:read", Resource: "users", Action: "read", Description: "Read user information"},
		{Name: "users:write", Resource: "users", Action: "write", Description: "Create and update users"},
		{Name: "users:delete", Resource: "users", Action: "delete", Description: "Delete users"},
		{Name: "admin:all", Resource: "admin", Action: "*", Description: "Full administrative access"},
	}

	for _, permission := range permissions {
		if err := db.FirstOrCreate(&permission, models.Permission{Name: permission.Name}).Error; err != nil {
			return fmt.Errorf("failed to create permission %s: %w", permission.Name, err)
		}
	}

	// Create default roles
	var adminPermissions []models.Permission
	db.Find(&adminPermissions)

	var deviceUserPermissions []models.Permission
	db.Where("resource IN ?", []string{"devices", "telemetry", "commands"}).Find(&deviceUserPermissions)

	var viewerPermissions []models.Permission
	db.Where("action = ?", "read").Find(&viewerPermissions)

	roles := []struct {
		role        models.Role
		permissions []models.Permission
	}{
		{
			role: models.Role{
				Name:        "admin",
				Description: "Full system administrator",
			},
			permissions: adminPermissions,
		},
		{
			role: models.Role{
				Name:        "device_manager",
				Description: "Device management and control",
			},
			permissions: deviceUserPermissions,
		},
		{
			role: models.Role{
				Name:        "viewer",
				Description: "Read-only access",
			},
			permissions: viewerPermissions,
		},
	}

	for _, roleData := range roles {
		var existingRole models.Role
		if err := db.Where("name = ?", roleData.role.Name).First(&existingRole).Error; err != nil {
			if err == gorm.ErrRecordNotFound {
				if err := db.Create(&roleData.role).Error; err != nil {
					return fmt.Errorf("failed to create role %s: %w", roleData.role.Name, err)
				}
				existingRole = roleData.role
			} else {
				return fmt.Errorf("failed to query role %s: %w", roleData.role.Name, err)
			}
		}

		// Associate permissions with role
		if err := db.Model(&existingRole).Association("Permissions").Replace(roleData.permissions); err != nil {
			return fmt.Errorf("failed to associate permissions with role %s: %w", roleData.role.Name, err)
		}
	}

	return nil
}
